package wechat

import (
	"errors"
	"fitness/internal"
	fitnessEntity "fitness/internal/entity/fitness"
	"fitness/internal/wechat/bo"
	"fitness/pkg/common"
	"fitness/pkg/constant"
	"fitness/pkg/tools"
	"fitness/pkg/utils/R"
	"fmt"
	"github.com/gin-gonic/gin"
	"go.uber.org/zap"
	"gorm.io/gorm"
	"net/http"
	"os"
	"path"
	"strconv"
	"strings"
	"time"
)

type MpService struct {
	logger    *zap.Logger
	Container *internal.Container
	wechat    *MiniApp
}

func NewMpService(logger *zap.Logger, container *internal.Container, wechat *MiniApp) *MpService {
	return &MpService{
		logger:    logger,
		Container: container,
		wechat:    wechat,
	}
}

func (m *MpService) WxLogin(c *gin.Context) {
	wpCode := bo.WpCode{}
	err := c.ShouldBindJSON(&wpCode)
	if err != nil || wpCode.ErrMsg == nil || *wpCode.ErrMsg != "login:ok" {
		c.JSON(http.StatusOK, R.ReturnFailMsg("获取微信code失败"))
		return
	}
	session, err := m.wechat.Code2Session(*wpCode.Code)
	if err != nil {
		c.JSON(http.StatusOK, R.ReturnFailMsg(err.Error()))
		return
	}
	openid := session.Openid
	user, err := m.Container.FitnessDaoContainer.UserDao.QueryFitnessUser(&openid)
	if err != nil && !errors.Is(err, gorm.ErrRecordNotFound) {
		c.JSON(http.StatusOK, R.ReturnFailMsg(err.Error()))
		return
	}
	//如果客户信息不存在，则进行创建
	if user.ID == nil {
		user, err = m.Container.FitnessDaoContainer.UserDao.CreateFitnessUser(&openid)
		if err != nil {
			c.JSON(http.StatusOK, R.ReturnFailMsg(err.Error()))
			return
		}
	}
	//根据用户信息创建token
	fitnessUser := make(map[string]any)
	fitnessUser[constant.ID] = user.ID
	fitnessUser[constant.OPEN_ID] = user.Openid
	//创建token
	token, _ := tools.MakeToken(fitnessUser)
	c.JSON(http.StatusOK, R.ReturnSuccessMsgData("ok", gin.H{
		"id":       user.ID,
		"username": user.Username,
		"nickname": user.Nickname,
		"avatar":   user.Avatar,
		"phone":    user.Phone,
		"openid":   user.Openid,
		"token":    token,
	}))
}

func (m *MpService) WxGetUserInfo(c *gin.Context) {
	userInfoParam := bo.WpGetUserInfo{}
	err := c.ShouldBindJSON(&userInfoParam)
	if err != nil {
		c.JSON(http.StatusOK, R.ReturnFailMsg("获取用户信息失败"))
		return
	}
	userInfo, err := m.wechat.GetUserInfo(
		*userInfoParam.SessionKey,
		*userInfoParam.RawData,
		*userInfoParam.EncryptedData,
		*userInfoParam.Signature,
		*userInfoParam.Iv)
	if err != nil {
		c.JSON(http.StatusOK, R.ReturnFailMsg(err.Error()))
		return
	}
	c.JSON(http.StatusOK, R.ReturnSuccess(userInfo))
}

// UploadAvatar 上传客户的头像图片
func (m *MpService) UploadAvatar(c *gin.Context) {
	//用id修改用户头像
	fitnessUserId := c.GetUint64(constant.ID)
	f, err := c.FormFile("imgfile")
	if err != nil {
		c.JSON(http.StatusOK, gin.H{
			"code": 400,
			"msg":  "上传失败!",
		})
		return
	} else {
		fileExt := strings.ToLower(path.Ext(f.Filename))
		isMainUploadExist, _ := tools.IsFileExist(common.Upload)
		if !isMainUploadExist {
			os.Mkdir(common.Upload, os.ModePerm)
		}
		fileName := tools.Md5(fmt.Sprintf("%s%s", f.Filename, time.Now().String()))
		fileDir := fmt.Sprintf("%s%d%s/", common.Upload, time.Now().Year(), time.Now().Month().String())
		isExist, _ := tools.IsFileExist(fileDir)
		if !isExist {
			os.Mkdir(fileDir, os.ModePerm)
		}
		filepath := fmt.Sprintf("%s%s%s", fileDir, fileName, fileExt)
		err := c.SaveUploadedFile(f, filepath)
		if err != nil {
			c.JSON(http.StatusOK, R.ReturnFailMsg("保存头像失败"))
			return
		}
		err = m.Container.FitnessDaoContainer.UserDao.UpdateAvatar(fitnessUserId, filepath)
		if err != nil {
			c.JSON(http.StatusOK, R.ReturnFailMsg("保存头像失败"))
			return
		}
		c.JSON(http.StatusOK, gin.H{
			"code": 200,
			"msg":  "上传成功!",
			"data": gin.H{
				"avatarUrl": filepath,
			},
		})
	}
}

func (m *MpService) Nickname(c *gin.Context) {
	//用id修改用户头像
	fitnessUserId := c.GetUint64(constant.ID)
	var user fitnessEntity.User
	err := c.ShouldBindJSON(&user)
	if err != nil {
		c.JSON(http.StatusOK, R.ReturnFailMsg(err.Error()))
		return
	}
	err = m.Container.FitnessDaoContainer.UserDao.UpdateNickname(fitnessUserId, user.Nickname)
	c.JSON(http.StatusOK, R.ReturnSuccess(nil))
}

func (m *MpService) GetDate(c *gin.Context) {
	// 获取今天的日期（忽略时分秒）
	now := time.Now()
	year, month, day := now.Date()
	today := time.Date(year, month, day, 0, 0, 0, 0, now.Location())

	// 存储一周的日期和星期信息
	days := make([]struct {
		Date    string // 日期格式：月/日
		Week    string // 星期几
		IsToday bool   // 是否是今天
	}, 7)

	// 星期几的中文映射
	weekMap := map[time.Weekday]string{
		time.Sunday:    "周日",
		time.Monday:    "周一",
		time.Tuesday:   "周二",
		time.Wednesday: "周三",
		time.Thursday:  "周四",
		time.Friday:    "周五",
		time.Saturday:  "周六",
	}
	//当前天
	currentDay := fmt.Sprintf("%d%02d%02d", today.Year(), today.Month(), today.Day())
	//转换成当天
	dayNum, _ := strconv.ParseUint(currentDay, 10, 64)
	var deleted = uint8(0)
	list, err := m.Container.FitnessDaoContainer.DetailDao.QueryDetailList(&dayNum, &deleted)
	if err != nil {
		c.JSON(http.StatusOK, R.ReturnFailMsg(err.Error()))
		return
	}
	// 生成今天及未来六天的信息
	for i := 0; i < 7; i++ {
		// 计算日期
		day := today.AddDate(0, 0, i)
		// 格式化日期：月/日（如：7/22）
		dateStr := fmt.Sprintf("%d", day.Day())
		// 获取星期几
		weekStr := weekMap[day.Weekday()]
		// 是否是今天
		isToday := i == 0

		days[i] = struct {
			Date    string
			Week    string
			IsToday bool
		}{dateStr, weekStr, isToday}
	}
	for _, day := range days {
		label := ""
		if day.IsToday {
			label = "（今日）"
		}
		fmt.Printf("%s | %s %s\n", day.Date, day.Week, label)
	}
	c.JSON(http.StatusOK, R.ReturnSuccess(gin.H{
		"days":    days,
		"details": list,
	},
	))
}

func (m *MpService) GetCourseList(c *gin.Context) {
	var detailQuery bo.DetailQuery
	err := c.ShouldBindJSON(&detailQuery)
	if err != nil {
		c.JSON(http.StatusOK, R.ReturnFailMsg(err.Error()))
		return
	}
	now := time.Now()
	year, month, day := now.Date()
	dayCurrent := time.Date(year, month, day, 0, 0, 0, 0, now.Location())
	today := dayCurrent.AddDate(0, 0, *detailQuery.FitnessDate)
	currentDay := fmt.Sprintf("%d%02d%02d", today.Year(), today.Month(), today.Day())
	//转换成当天
	dayNum, _ := strconv.ParseUint(currentDay, 10, 64)

	list, err := m.Container.FitnessDaoContainer.DetailDao.QueryDetailList(
		&dayNum,
		detailQuery.IsDeleted)
	if err != nil {
		c.JSON(http.StatusOK, R.ReturnFailMsg(err.Error()))
		return
	}
	c.JSON(http.StatusOK, R.ReturnSuccess(list))
}

func (m *MpService) Phone(c *gin.Context) {
	//用id修改用户头像
	fitnessUserId := c.GetUint64(constant.ID)
	var user fitnessEntity.User
	err := c.ShouldBindJSON(&user)
	if err != nil {
		c.JSON(http.StatusOK, R.ReturnFailMsg(err.Error()))
		return
	}
	err = m.Container.FitnessDaoContainer.UserDao.UpdatePhone(fitnessUserId, user.Phone)
	c.JSON(http.StatusOK, R.ReturnSuccess(nil))
}
